<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>计算机网络</title>
    <meta name="generator" content="VuePress 1.5.2">
    <link rel="apple-touch-icon" href="/blog/apple-touch-icon.png">
    <link rel="icon" href="/blog/favicon.ico">
    <link rel="manifest" href="/blog/manifest.json">
    <link rel="mask-icon" href="/blog/icons/icon.svg" color="#ffffff">
    <meta name="description" content="可能是全网最给力的前端博客">
    <meta name="theme-color" content="#ffffff">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="default">
    <meta name="msapplication-TileImage" content="/icons/icon-144x144.png">
    <link rel="preload" href="/blog/assets/css/0.styles.3f022aa2.css" as="style"><link rel="preload" href="/blog/assets/js/app.7fa07907.js" as="script"><link rel="preload" href="/blog/assets/js/2.b6c629b5.js" as="script"><link rel="preload" href="/blog/assets/js/20.6db77b80.js" as="script"><link rel="preload" href="/blog/assets/js/3.589a64d8.js" as="script"><link rel="prefetch" href="/blog/assets/js/10.8e59817e.js"><link rel="prefetch" href="/blog/assets/js/11.34cd2f44.js"><link rel="prefetch" href="/blog/assets/js/12.445d88fd.js"><link rel="prefetch" href="/blog/assets/js/13.e6460b96.js"><link rel="prefetch" href="/blog/assets/js/14.d0ee0fcf.js"><link rel="prefetch" href="/blog/assets/js/15.e91e78ed.js"><link rel="prefetch" href="/blog/assets/js/16.8310de97.js"><link rel="prefetch" href="/blog/assets/js/17.6ca97902.js"><link rel="prefetch" href="/blog/assets/js/18.a82e534b.js"><link rel="prefetch" href="/blog/assets/js/19.e3a914ee.js"><link rel="prefetch" href="/blog/assets/js/21.e705be23.js"><link rel="prefetch" href="/blog/assets/js/22.efc39414.js"><link rel="prefetch" href="/blog/assets/js/23.5f4c7658.js"><link rel="prefetch" href="/blog/assets/js/24.359a62e4.js"><link rel="prefetch" href="/blog/assets/js/25.c49ef7a9.js"><link rel="prefetch" href="/blog/assets/js/26.8d45691f.js"><link rel="prefetch" href="/blog/assets/js/27.364b1802.js"><link rel="prefetch" href="/blog/assets/js/28.485228c0.js"><link rel="prefetch" href="/blog/assets/js/29.0ee8fc42.js"><link rel="prefetch" href="/blog/assets/js/30.465920a7.js"><link rel="prefetch" href="/blog/assets/js/4.1dbf8b08.js"><link rel="prefetch" href="/blog/assets/js/5.9253897f.js"><link rel="prefetch" href="/blog/assets/js/6.4c1f13e3.js"><link rel="prefetch" href="/blog/assets/js/7.bc1839d0.js"><link rel="prefetch" href="/blog/assets/js/8.611f5ec5.js"><link rel="prefetch" href="/blog/assets/js/9.5813bb74.js">
    <link rel="stylesheet" href="/blog/assets/css/0.styles.3f022aa2.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blog/" class="home-link router-link-active"><img src="/blog/app.png" alt="卢本伟广场" class="logo"> <span class="site-name can-hide">卢本伟广场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/dsm/" class="nav-link router-link-active">
  歪比歪比
</a></div><div class="nav-item"><a href="/blog/lbw/" class="nav-link">
  歪比吧卜
</a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/blog/dsm/" class="nav-link router-link-active">
  歪比歪比
</a></div><div class="nav-item"><a href="/blog/lbw/" class="nav-link">
  歪比吧卜
</a></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/blog/dsm/" aria-current="page" class="sidebar-link">前端自我修养</a></li><li><a href="/blog/dsm/algorithm.html" class="sidebar-link">前端面试算法</a></li><li><a href="/blog/dsm/jsbook.html" class="sidebar-link">JS高级程序设计</a></li><li><a href="/blog/dsm/cc150.html" class="sidebar-link">程序员面试金典</a></li><li><a href="/blog/dsm/pattern.html" class="sidebar-link">前端设计模式</a></li><li><a href="/blog/dsm/architecture.html" class="sidebar-link">前端工程架构</a></li><li><a href="/blog/dsm/protocol.html" aria-current="page" class="active sidebar-link">计算机网络</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#从url到页面的过程" class="sidebar-link">从URL到页面的过程</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#页面加载过程" class="sidebar-link">页面加载过程</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#页面渲染过程" class="sidebar-link">页面渲染过程</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#tcp-ip-网络模型" class="sidebar-link">TCP/IP 网络模型</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#http概述" class="sidebar-link">HTTP概述</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#dns-cdn" class="sidebar-link">DNS / CDN</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#http报文" class="sidebar-link">HTTP报文</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#请求报文" class="sidebar-link">请求报文</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#http请求方法" class="sidebar-link">HTTP请求方法</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#get与post区别" class="sidebar-link">GET与POST区别</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#响应报文" class="sidebar-link">响应报文</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#状态码" class="sidebar-link">状态码</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#长连接-管线化" class="sidebar-link">长连接 / 管线化</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#https概述" class="sidebar-link">HTTPS概述</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#http与https区别" class="sidebar-link">HTTP与HTTPS区别</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#https加密流程" class="sidebar-link">HTTPS加密流程</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#http-2概述" class="sidebar-link">HTTP/2概述</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#tcp-udp" class="sidebar-link">TCP / UDP</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#tcp握手为什么要三次" class="sidebar-link">TCP握手为什么要三次</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#tcp挥手为什么要四次" class="sidebar-link">TCP挥手为什么要四次</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#第四次挥手客户端等待2msl的意义" class="sidebar-link">第四次挥手客户端等待2MSL的意义</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#tcp怎么保证传输可靠性" class="sidebar-link">TCP怎么保证传输可靠性</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#arq协议" class="sidebar-link">ARQ协议</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#流量控制" class="sidebar-link">流量控制</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#拥塞控制" class="sidebar-link">拥塞控制</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/protocol.html#服务端推送" class="sidebar-link">服务端推送</a></li></ul></li><li><a href="/blog/dsm/node.html" class="sidebar-link">服务端开发</a></li><li><a href="/blog/dsm/webgl.html" class="sidebar-link">图形3D开发</a></li><li><a href="/blog/dsm/lodash.html" class="sidebar-link">jQuery / Lodash</a></li><li><a href="/blog/dsm/finger.html" class="sidebar-link">AlloyFinger</a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="计算机网络"><a href="#计算机网络" class="header-anchor">#</a> 计算机网络</h1> <img id="topImg" src="/blog/images/IMG_9024.jpg"> <h2 id="从url到页面的过程"><a href="#从url到页面的过程" class="header-anchor">#</a> 从URL到页面的过程</h2> <ul><li>构建请求行</li> <li>查找强缓存</li> <li>查找DNS缓存/DNS解析/缓存IP地址</li> <li>TCP队列等待/TCP连接</li> <li>发起HTTP请求</li> <li>资源响应/重定向处理</li> <li>协商缓存/缓存资源</li> <li>TCP重用连接/TCP断开连接</li> <li>页面渲染</li></ul> <h2 id="页面加载过程"><a href="#页面加载过程" class="header-anchor">#</a> 页面加载过程</h2> <ul><li>浏览器创建Document对象，GUI线程开始解析HTML文档来构造DOM树。这个阶段为document.readyState = 'loading'
<ul><li>遇到link外部css，异步加载css文件构建CSSOM树</li> <li>遇到script外部js，并且没有设置async、defer，浏览器同步加载并执行该js文件</li> <li>遇到script外部js，并且设置有async、defer，浏览器异步加载该js文件。对于async属性的脚本，脚本加载完成后立即执行</li> <li>遇到图片视频等外部资源，浏览器异步加载src</li></ul></li> <li>当文档解析完成，能访问完整DOM树，document.readyState = 'interactive'，document对象触发DOMContentLoaded事件，所有defer的js脚本会按照加载顺序执行</li> <li>浏览器利用DOM树和CSSOM树构建布局树，根据布局树绘制页面</li> <li>当所有文档和所有图片视频等资源异步加载完成后，document.readyState='complete'，window对象触发onload事件</li></ul> <h2 id="页面渲染过程"><a href="#页面渲染过程" class="header-anchor">#</a> 页面渲染过程</h2> <ul><li>解析HTML资源
<ul><li>根据指定编码标准将字节流解码成字符流</li> <li>根据W3C标准将字符解释成令牌</li> <li>令牌转换成DOM节点</li> <li>用DOM节点构建DOM树</li></ul></li> <li>样式计算
<ul><li>解析CSS样式表</li> <li>构建styleSheets对象</li> <li>标准化styleSheets对象中的属性值</li> <li>计算并构建ComputedStyle对象和CSSOM树</li></ul></li> <li>构建布局树
<ul><li>根据DOM树和CSSOM树构建布局树</li></ul></li> <li>计算布局树
<ul><li>计算布局树节点几何坐标位置</li> <li>根据样式属性构建图层树</li></ul></li> <li>绘制布局树
<ul><li>生成绘制列表</li> <li>合成线程根据视口把图层划分为多个图块</li> <li>栅格化线程池使用GPU将图块转换成位图并保存到显存中</li></ul></li> <li>合并所有图层</li> <li>主线程展示显存内容到页面</li></ul> <h2 id="tcp-ip-网络模型"><a href="#tcp-ip-网络模型" class="header-anchor">#</a> TCP/IP 网络模型</h2> <p>TCP/IP传输控制协议/因特网协议（Transmission Control Protocol/Internet Protocol）是一个能让互联网运作起来的协议集合，由两个最具代表性的协议TCP协议和IP协议命名，这整套协议规定了设备该怎么通过互联网跟其他设备交换数据，定义了数据该怎么被打包、发送、路由、接收。这套协议的功能被分为四层：应用层、传输层、网络层、链路层。</p> <ul><li><p>应用层：向用户提供通信服务，比如，FTP、HTTP、HTTPS、WebSocket、DNS、Telnet、SSH、SMTP等</p></li> <li><p>传输层：负责IP地址间的数据传输，比如，TCP和UDP</p></li> <li><p>网络层：负责数据包的路由，比如，IP</p></li> <li><p>链路层：负责在以太网、WiFi 这样的底层网络上发送数据包</p></li></ul> <p>TCP/IP四层网络模型之后有了国际化标准组织制定的OSI七层网络模型，由于后者过于理想化没有取得市场化的成功，TCP/IP模型则被作为真正的国际标准。</p> <h2 id="http概述"><a href="#http概述" class="header-anchor">#</a> HTTP概述</h2> <p>HTTP超文本传输协议（Hypertext Transfer Protocol）一句话来说就是，两台电脑间传输文字、图片、音频、视频等超文本数据的规范，是浏览器和服务器请求和应答的标准。</p> <ul><li>HTTP 协议始于三十年前蒂姆·伯纳斯 - 李的一篇论文</li> <li>HTTP/0.9 是个简单的文本协议，只能获取文本资源</li> <li>HTTP/1.0 确立了大部分现在使用的技术，1996年正式发布，RFC文档编号为1945，但它没成为正式标准</li> <li>HTTP/1.1 是目前互联网上使用最广泛的协议，功能也非常完善，1999年发布了RFC 2616文档，成为正式标准，最后2014年修订成6份较小的RFC文档，编号为7230-7235</li> <li>HTTP/2 基于 Google 的 SPDY 协议，注重性能改善，但还未普及</li> <li>HTTP/3 基于 Google 的 QUIC 协议，是将来的发展方向</li></ul> <p>HTTP 通常跑在 TCP/IP 协议栈之上，依靠 IP 协议实现寻址和路由、TCP 协议实现可靠数据传输、DNS 协议实现域名查找、SSL/TLS 协议实现安全通信。此外，还有一些协议依赖于 HTTP，例如 WebSocket、HTTPDNS 等。</p> <h2 id="dns-cdn"><a href="#dns-cdn" class="header-anchor">#</a> DNS / CDN</h2> <p>Domain Name System 域名系统协议负责将域名解析成对应的IP地址。</p> <p>DNS 的核心系统是一个三层的树状、分布式服务，基本对应域名的结构：</p> <ul><li>根域名服务器（Root DNS Server）：管理顶级域名服务器，返回“com”“net”“cn”等顶级域名服务器的 IP 地址</li> <li>顶级域名服务器（Top-level DNS Server）：管理各自域名下的权威域名服务器，比如 com 顶级域名服务器可以返回 apple.com 域名服务器的 IP 地址</li> <li>权威域名服务器（Authoritative DNS Server）：管理自己域名下主机的 IP 地址，比如 apple.com 权威域名服务器可以返回 www.apple.com 的 IP 地址</li></ul> <p>如果全世界都往这个系统挤，即使不瘫痪也会很卡，为了缓解域名解析系统的压力，我们需要缓存IP地址来进行优化：</p> <ul><li>浏览器缓存</li> <li>操作系统缓存（hosts文件）</li> <li>路由器缓存</li> <li>服务商ISP DNS缓存</li> <li>DNS查询（递归查询 / 迭代查询）</li></ul> <p>DNS解析可能会重定向到能够最快响应用户的一个CDN服务器的IP地址，这样拿到的就会是CDN代理服务器而不是目标网站的实际地址，CDN服务器会代替源站服务器响应客户端的请求，缩短了用户查看内容的访问延迟，提高了用户访问网站的响应速度与网站的可用性，解决了网络带宽小、用户访问量大、网点分布不均等问题</p> <p>“Content Delivery Network”，翻译过来就是“内容分发网络”。它应用了 HTTP 协议里的缓存和代理技术，代替源站响应客户端的请求，主要起到缓存加速的作用。因为CDN会缓存网站的大部分资源，比如图片、CSS 样式表，所以有的 HTTP 请求就不需要再发到目标服务器，CDN就可以直接响应请求，返回数据。</p> <h2 id="http报文"><a href="#http报文" class="header-anchor">#</a> HTTP报文</h2> <p>HTTP不管传输的具体过程，只管它传输的报文内容，在规范文档里详细定义了报文的格式，规定了组成部分，解析规则，还有处理策略，所以可以在 TCP/IP 层之上实现更灵活丰富的功能，例如连接控制，缓存管理、数据编码、内容协商等等。</p> <p>HTTP 协议的请求报文和响应报文的结构基本相同，由三大部分组成：</p> <ul><li>起始行（start line）：描述请求或响应的基本信息</li> <li>头部字段集合（header）：使用 key-value 形式更详细地说明报文，不能含空格，不区分大小写，顺序任意，可以任意添加自定义字段，实现功能扩展</li> <li>消息正文（entity）：实际传输的数据，它不一定是纯文本，可以是图片、视频等二进制数据</li></ul> <p>HTTP 协议规定报文必须有 header，但可以没有 body，而且在 header 之后必须要有一个“空行”</p> <h2 id="请求报文"><a href="#请求报文" class="header-anchor">#</a> 请求报文</h2> <ul><li>请求行，比如：GET / HTTP/1.1
<ul><li>请求方法：是一个动词，如 GET/POST，表示对资源的操作</li> <li>请求目标：通常是一个 URI，标记了请求方法要操作的资源</li> <li>版本号：表示报文使用的 HTTP 协议版本</li></ul></li> <li>请求头：进一步说明请求信息或者额外的附加条件
<ul><li>Host：唯一的一个必须出现的字段，告诉服务器这个请求应该由哪个主机来处理</li> <li>User-Agent：描述发起HTTP请求的客户端，服务器可以依据它来返回最合适此浏览器显示的页面。</li> <li>Accept：客户端希望接受的数据类型</li> <li>Accept-Encoding：告诉服务端使用什么的方式来进行压缩（gzip、deflate、br）</li> <li>Content-Type：发送端（客户端|服务器）发送的实体数据的数据类型</li> <li>Accept-Ranges（HTTP/1.1）：是否支持范围请求</li> <li>If-Modified-Since：资源是否在某个时间点更新过（协商缓存）</li> <li>If-None-Match（HTTP/1.1）：服务端资源的ETag是否不等于某个值（协商缓存）</li></ul></li> <li>空行</li> <li>请求体</li></ul> <h2 id="http请求方法"><a href="#http请求方法" class="header-anchor">#</a> HTTP请求方法</h2> <p>目前 HTTP/1.1 规定了八种方法，单词都必须是大写的形式：</p> <ul><li>GET：获取资源</li> <li>HEAD：获取资源的响应头</li> <li>POST：提交新数据</li> <li>PUT：更新旧数据</li> <li>DELETE：删除资源</li> <li>CONNECT：建立特殊的连接隧道</li> <li>OPTIONS：列出可对资源实行的方法</li> <li>TRACE：追踪请求到响应的传输路径</li></ul> <p>对于高机密文件，服务器就可以有如下的几种响应方式：</p> <ul><li>假装这个文件不存在，直接返回一个 404 Not found 报文；</li> <li>稍微友好一点，明确告诉你有这个文件，但不允许访问，返回一个 403 Forbidden；</li> <li>再宽松一些，返回 405 Method Not Allowed，然后用 Allow 头告诉你可以用 HEAD 方法获取文件的元信息。</li></ul> <h2 id="get与post区别"><a href="#get与post区别" class="header-anchor">#</a> GET与POST区别</h2> <p>如果基于RFC规范，GET和POST具有相同语法，但是有不同语义，GET获取数据，POST提交新数据，其他方面没有区别。</p> <p>如果是在浏览器里：</p> <ul><li>GET请求参数在URL可见，POST请求参数在请求体可见，但网络上传输时都是明文的</li> <li>GET请求长度由浏览器URL长度限制 ，POST请求长度由服务器设定</li> <li>GET请求到的URL可以收藏，POST不可以</li> <li>GET请求参数会被保存在浏览器历史记录里，POST不会</li> <li>GET请求会被浏览器缓存，POST不会</li> <li>GET请求后，后退刷新无影响，POST请求会重新提交</li> <li>GET请求参数的数据类型只接受ASCII字符，POST没有限制</li> <li>GET请求只能进行URL编码，而POST支持多种编码方式</li> <li>GET比POST更不安全，因为请求参数直接暴露在URL里，不能有敏感信息</li> <li>GET多用于幂等场景如搜索，POST多用于不幂等场景如注册（幂等：同一个请求方法执行多次和仅执行一次的效果完全相同）</li></ul> <h2 id="响应报文"><a href="#响应报文" class="header-anchor">#</a> 响应报文</h2> <ul><li>状态行，比如：HTTP/1.1 200 OK
<ul><li>版本号：表示报文使用的 HTTP 协议版本</li> <li>状态码：一个三位数，用代码的形式表示处理的结果，比如 200 是成功，500 是服务器错误</li> <li>原因：作为数字状态码补充，是更详细的解释文字，帮助人理解原因。</li></ul></li> <li>响应头：补充说明响应报文的信息
<ul><li>Date：一个通用字段，但通常出现在响应头里，表示 HTTP 报文创建的时间，客户端可以使用这个时间再搭配其他字段决定缓存策略</li> <li>Server：只能出现在响应头里，它告诉客户端当前正在提供 Web 服务的软件名称和版本号</li> <li>Content-Encoding：对应 Accept-Encoding，指服务端到底使用的是哪种压缩方式</li> <li>Content-Type：对应Accept，从请求中的Accept支持的数据格式中选一种来返回</li> <li>Transfer-Encoding：传输编码</li> <li>Content-Range（HTTP/1.1）：返回的数据内容的起止位置以及整个需要请求的内容的长度</li> <li>Cache-Control（HTTP/1.1）：设置资源缓存策略（强缓存）</li> <li>Last-Modified：资源在服务器上的最后修改时间（协商缓存）</li> <li>ETag（HTTP/1.1）：资源文件唯一标识值（协商缓存）</li></ul></li> <li>空行</li> <li>响应体</li></ul> <h2 id="状态码"><a href="#状态码" class="header-anchor">#</a> 状态码</h2> <p>RFC标准把状态码规定成是三位数，分成五类，用数字的第一位表示分类，而 0~99 不用，范围就是100~599</p> <p>这五类的具体含义是：</p> <p>1××：提示信息，表示目前是协议处理的中间状态，还需要后续的操作</p> <p>2××：成功，报文已经收到并被正确处理</p> <ul><li>“200 OK”：表示请求成功，服务器如客户端所期望的那样返回了处理结果</li> <li>“204 No Content”：表示请求成功，但响应体没数据</li> <li>“206 Partial Content”：表示请求成功，但响应体只有部分数据</li></ul> <p>3××：重定向，资源位置发生变动，需要客户端重新发送请求</p> <ul><li>“301 Moved Permanently”：永久重定向，此次请求的资源已经不存在了，需要改用改用新的 URI 再次访问</li> <li>“302 Found”：临时重定向，意思是请求的资源还在，但需要暂时用另一个 URI 来访问</li> <li>“304 Not Modified” ：缓存重定向，资源未修改，用于缓存控制</li></ul> <p>4××：客户端错误，请求报文有误，服务器无法处理</p> <ul><li>“400 Bad Request”：请求报文有错误，但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说，只是一个笼统的错误</li> <li>“403 Forbidden”：服务器禁止访问资源。原因可能多种多样，例如信息敏感、法律禁止等，如果服务器友好一点，可以在 body 里详细说明拒绝请求的原因</li> <li>“404 Not Found”：资源在本服务器上未找到</li></ul> <p>5××：服务器错误，服务器在处理请求时内部发生了错误</p> <ul><li>“500 Internal Server Error”：服务器端在执行请求时发生了错误</li> <li>“501 Not Implemented”：客户端请求的功能还不支持</li> <li>“502 Bad Gateway”：服务器作为网关或者代理时发生了错误</li> <li>“503 Service Unavailable”：服务器当前很忙，暂时无法响应服务</li></ul> <h2 id="长连接-管线化"><a href="#长连接-管线化" class="header-anchor">#</a> 长连接 / 管线化</h2> <p>HTTP 协议最初（0.9/1.0）是个非常简单的协议，通信过程也采用了简单的“请求 - 应答”方式。它底层的数据传输基于 TCP/IP，每次发送请求前需要先与服务器建立连接，收到响应报文后会立即关闭连接。</p> <p>因为客户端与服务器的整个连接过程很短暂，不会与服务器保持长时间的连接状态，所以就被称为“短连接”（short-lived connections），短连接的缺点相当严重，因为在 TCP 协议里，建立连接和关闭连接都是非常“昂贵”的操作。</p> <p>HTTP 协议就提出了“长连接”的通信方式，也叫“持久连接”（persistent connections）、“连接保活”（keep alive）、“连接复用”（connection reuse）。</p> <p>HTTP/1.1 默认启用长连接，在一个连接上收发多个请求响应，提高了传输效率，我们也可以在请求头里明确地要求使用长连接机制，使用的字段是Connection，值是“keep-alive”。不过不管客户端是否显式要求长连接，如果服务器支持长连接，它总会在响应报文里放一个“Connection: keep-alive”字段</p> <p>因为 TCP 连接长时间不关闭，服务器必须在内存里保存它的状态，这就占用了服务器的资源。如果有大量的空闲长连接只连不发，就会很快耗尽服务器的资源，导致服务器无法为真正有需要的用户提供服务。</p> <p>所以，长连接也需要在恰当的时间关闭，不能永远保持与服务器的连接，这在客户端或者服务器都可以做到。</p> <ul><li>在客户端，可以在请求头里加上“Connection: close”字段</li> <li>在Nginx服务端，使用keepalive_timeout设置长连接的超时时间</li></ul> <p>在永久连接的基础上，HTTP/1.1还提供了管线化(pipelining)机制，请求和响应不再是依次交替的了，它可以支持一次性发送多个请求，并一次性接收多个响应，那么持久连接和管线化的区别在于：</p> <ul><li><p>持久连接的一个缺点是请求和响应式是顺序执行的，只有在请求1的响应收到之后，才会发送请求2，而管线化不需要等待上一次请求得到响应就可以进行下一次请求。实现并行发送请求</p></li> <li><p>只有GET和HEAD要求可以进行管线化，而POST则有所限制</p></li> <li><p>初次创建连接时也不应启动管线机制，因为对方（服务器）不一定支持HTTP/1.1版本的协议</p></li> <li><p>HTTP1.1要求服务器端支持管线化，但并不要求服务器端也对响应进行管线化处理，只是要求对于管线化的请求不失败，而且现在很多服务器端和代理程序对管线化的支持并不好，现代浏览器Chrome和Firefox默认并未开启管线化支持</p></li></ul> <p>然而pipelining在接收response返回时，也必须依顺序接收，如果前一个请求遇到了阻塞，后面的请求即使已经处理完毕了，仍然需要等待阻塞的请求处理完毕，这也就是队头阻塞(Head of line blocking)，因为“请求 - 应答”模型不能变，所以“队头阻塞”问题在 HTTP/1.1 里无法解决，只能缓解：</p> <ul><li>并发连接（concurrent connections）：同时对一个域名发起多个长连接，用数量来解决质量的问题</li> <li>域名分片（domain sharding）：开启多个域名，这些域名都指向同一台服务器，这样长连接的数量就又上去了</li></ul> <h2 id="https概述"><a href="#https概述" class="header-anchor">#</a> HTTPS概述</h2> <p>HTTPS是在HTTP上建立SSL加密层，并对传输数据进行加密，是HTTP协议的安全版，所谓HTTPS，其实就是身披SSL协议这层外壳的HTTP，现在它被广泛用于万维网上安全敏感的通讯，例如交易支付方面。</p> <p>HTTPS所解决的HTTP协议中存在的问题：</p> <ul><li>HTTP报文使用明文（指未经过加密的报文）方式发送，内容可能被窃听（对称加密+非对称加密）</li> <li>HTTP无法证明报文的完整性，所以可能遭篡改，没有任何办法确认，发出的请求/响应和接收到的请求/响应是前后相同的（数字签名）</li> <li>HTTP协议中的请求和响应不会对通信方进行确认，不验证通信方的身份，因此有可能遭遇伪装（数字证书）</li></ul> <h2 id="http与https区别"><a href="#http与https区别" class="header-anchor">#</a> HTTP与HTTPS区别</h2> <ul><li>默认端口：HTTP：80端口；HTTPS：443端口</li> <li>数据传输：HTTP数据明文传输；HTTPS数据加密传输</li> <li>应用传输：HTTP工作于应用层；HTTPS工作在传输层</li> <li>地址协议：http://开头（浏览器情况下显示问号、不安全）；https://开头（浏览器展示安全，绿色小锁图标）</li> <li>协议门槛：HTTP不需要证书；HTTPS需要Gworg机构颁发SSL证书</li> <li>网站安全：HTTP很容易被劫持，打开网页甚至直接跳转到另一个网站；HTTPS加密安全，不被劫持，交易传输数据加密。</li> <li>真假网站：HTTP很容易被复制；HTTPS网站使用OV或EV证书必须要实名，而且要求很严格，尤其是EV证书，地址栏将直接展示名称，比如各大银行的网站</li></ul> <h2 id="https加密流程"><a href="#https加密流程" class="header-anchor">#</a> HTTPS加密流程</h2> <ul><li>Client发起一个HTTPS的请求，根据RFC2818的规定，Client知道需要连接Server的443（默认）端口</li> <li>Server把事先配置好的公钥证书（public key certificate）返回给客户端</li> <li>Client验证公钥证书：比如是否在有效期内，证书的用途是不是匹配Client请求的站点，是不是在CRL吊销列表里面，它的上一级证书是否有效，这是一个递归的过程，直到验证到根证书（操作系统内置的Root证书或者Client内置的Root证书）。如果验证通过则继续，不通过则显示警告信息</li> <li>Client使用伪随机数生成器生成加密所使用的对称密钥，然后用证书的公钥加密这个对称密钥，发给Server</li> <li>Server使用自己的私钥（private key）解密这个消息，得到对称密钥。至此，Client和Server双方都持有了相同的对称密钥</li> <li>Server使用对称密钥加密“明文内容A”，发送给Client</li> <li>Client使用对称密钥解密响应的密文，得到“明文内容A”</li> <li>Client再次发起HTTPS的请求，使用对称密钥加密请求的“明文内容B”，然后Server使用对称密钥解密密文，得到“明文内容B”</li></ul> <h2 id="http-2概述"><a href="#http-2概述" class="header-anchor">#</a> HTTP/2概述</h2> <p>由于 HTTPS 已经在安全方面做的非常好了，所以 HTTP/2 的唯一目标就是改进性能。</p> <p>因为必须要保持功能上的兼容，所以 HTTP/2 把 HTTP 分解成了“语义”和“语法”两个部分，“语义”层不做改动，与 HTTP/1 完全一致（即 RFC7231）。比如请求方法、URI、状态码、头字段等概念都保留不变，这样就消除了再学习的成本，基于 HTTP 的上层应用也不需要做任何修改，可以无缝转换到 HTTP/2。HTTP/2 在“语法”层做了“天翻地覆”的改造，完全变更了HTTP报文的传输方式。</p> <ul><li>头部压缩：HTTP/2使用了HPACK”算法来压缩报文头header，在客户端和服务器两端建立“字典”，用索引号表示重复的字符串，还釆用哈夫曼编码来压缩整数和字符串，可以达到 50%~90% 的高压缩率</li> <li>二进制格式：HTTP/2中引入了新的编码机制，所有传输的数据都会被分割，并采用二进制格式编码，大大方便了计算机的解析</li> <li>多路复用：同域名下所有通信都在单个TCP连接上完成，一个TCP连接中可以存在多条流（二进制帧的双向传输序列），消息碎片数据帧在流里按照次序组装起来就是 HTTP/1 里的请求报文和响应报文，实现了在单个TCP连接上并行交错的请求和响应，之间互不干扰，也就不会出现“队头阻塞”问题，降低了延迟，大幅度提高了连接的利用率，但由于TCP独有的丢包重传机制，如果网络质量比较差丢了包，TCP就只能把已经收到的包暂存起来，还是会发生队头阻塞问题</li> <li>服务器推送：服务器不再是完全被动地响应请求，也可以新建“流”主动向客户端发送消息。比如，在浏览器刚请求 HTML 的时候就提前把可能会用到的 JS、CSS 文件发给客户端，减少等待的延迟</li></ul> <h2 id="tcp-udp"><a href="#tcp-udp" class="header-anchor">#</a> TCP / UDP</h2> <p>运输层为主机的应用进程提供端到端的逻辑通信服务，包含TCP和UDP协议，依据端口号实现一个主机多个进程的通信。</p> <ul><li><p>如果使用UDP协议，则无需事先建立连接，只需要按照需求把数据发送出去即可，在网络质量较差的环境，数据丢包严重也不重传，没有流量控制和拥塞控制</p></li> <li><p>如果使用TCP协议，发送数据需要事先建立全双工可靠信道（TCP三次握手），之后再将数据可靠地发送到接收端</p></li></ul> <p>TCP和UDP区别：</p> <ul><li>TCP面向连接（如打电话要先拨号建立连接）；UDP是无连接的，即发送数据之前不需要建立连接</li> <li>TCP提供可靠的服务。也就是说，通过TCP连接传送的数据，无差错，不丢失，不重复，且按序到达；UDP尽最大努力交付，即不保证可靠交付</li> <li>TCP连接只能是一对一通信；UDP支持一对一，一对多，多对一和多对多的交互通信</li> <li>TCP首部开销20字节；UDP的首部开销小，只有8个字节</li> <li>TCP的逻辑通信信道是全双工的可靠信道；UDP则是不可靠信道</li> <li>TCP面向字节流，实际上是TCP把数据看成一连串无结构的字节流，如文件传输；UDP是面向报文的，没有拥塞控制，因此网络出现拥塞不会使源主机的发送速率降低，如IP电话，实时视频会议等</li></ul> <h2 id="tcp握手为什么要三次"><a href="#tcp握手为什么要三次" class="header-anchor">#</a> TCP握手为什么要三次</h2> <p>只有三次握手才能确认双方的接收与发送能力是否正常：</p> <ul><li>第一次握手：客户端发送网络包，服务端收到了。这样服务端就能得出结论：客户端的发送能力、服务端的接收能力是正常的</li> <li>第二次握手：服务端发包，客户端收到了。这样客户端就能得出结论：服务端的接收、发送能力，客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常</li> <li>第三次握手：客户端发包，服务端收到了。这样服务端就能得出结论：客户端的接收、发送能力正常，服务器自己的发送、接收能力也正常</li></ul> <p>如果只用两次握手则会出现以下情况：</p> <p>如客户端发出连接请求，但因连接请求报文丢失而未收到确认，于是客户端再重传一次连接请求。后来收到了确认，建立了连接（脏连接）。数据传输完毕后，就释放了连接，客户端共发出了两个连接请求报文段，其中第一个丢失，第二个到达了服务端，但是第一个丢失的报文段只是在某些网络结点长时间滞留了，延误到连接释放以后的某个时间才到达服务端，此时服务端误认为客户端又发出一次新的连接请求，于是就向客户端发出确认报文段，同意建立连接，不采用三次握手，只要服务端发出确认，就建立新的连接了，此时客户端忽略服务端发来的确认，也不发送数据，而服务端一直等待客户端发送数据，浪费资源。</p> <p>所以，TCP只能是三次握手，不能是两次，也不能是四次。少于三次握手，不能保证是否建立了通信连接；多于三次握手，都是徒劳和浪费的。</p> <h2 id="tcp挥手为什么要四次"><a href="#tcp挥手为什么要四次" class="header-anchor">#</a> TCP挥手为什么要四次</h2> <p>只有四次挥手才能确保数据完整传输不重传：</p> <ul><li>第一次挥手：客户端向服务端发送连接释放报文，并停止发送数据</li> <li>第二次挥手：服务端收到连接释放报文，发出确认报文，但TCP是双向连接的，所以服务端仍然可以给客户端发数据</li> <li>第三次挥手：服务器将最后的数据发送完毕后，就向客户端发送连接释放报文</li> <li>第四次挥手：客户端收到服务器的连接释放报文后发出确认报文</li></ul> <p>TCP四次挥手也可以分为两个阶段：</p> <p><strong>第一：客户端至服务器的半双工连接关闭</strong></p> <p>客户端向服务器发送FIN信号，进入FIN_WAIT1的状态，等待服务器的ACK信号
收到服务器的ACK后，进入FIN_WAIT2</p> <p><strong>第二：服务器至客户端的半双工连接关闭</strong></p> <p>客户端收到服务器发来的FIN后，发送ACK，并进入TIME_WAIT，等待2MSL，若无异常，则客户端连接关闭
服务器收到客户端发来的ACK后，关闭连接</p> <h2 id="第四次挥手客户端等待2msl的意义"><a href="#第四次挥手客户端等待2msl的意义" class="header-anchor">#</a> 第四次挥手客户端等待2MSL的意义</h2> <p>MSL是报文最大生存时间，它是任何报文在网络上存在的最长时间，超过这个时间报文将被丢弃。</p> <p>为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失，从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK，接着客户端再重传一次确认，重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL，而是在发送完ACK之后直接释放关闭，一但这个ACK丢失的话，服务器就无法正常的进入关闭连接状态。</p> <ul><li><strong>保证客户端发送的最后一个ACK报文段能够到达服务端：</strong> 这个ACK报文段有可能丢失，使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认，服务端超时重传FIN+ACK报文段，而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段，接着客户端重传一次确认，重新启动2MSL计时器，最后客户端和服务端都进入到CLOSED状态，若客户端在TIME-WAIT状态不等待一段时间，而是发送完ACK报文段后立即释放连接，则无法收到服务端重传的FIN+ACK报文段，所以不会再发送一次确认报文段，则服务端无法正常进入到CLOSED状态</li> <li><strong>防止“已失效的连接请求报文段”出现在新连接中：</strong> 客户端在发送完最后一个ACK报文段后，再经过2MSL，就可以使本连接持续的时间内所产生的所有报文段都从网络中消失，使下一个新的连接中不会出现这种遗留下来的旧的连接请求报文段</li></ul> <h2 id="tcp怎么保证传输可靠性"><a href="#tcp怎么保证传输可靠性" class="header-anchor">#</a> TCP怎么保证传输可靠性</h2> <ul><li><strong>校验和：</strong> TCP 将保持它首部和数据的检验和。这是一个端到端的检验和，目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错，TCP 将丢弃这个报文段和不确认收到此报文段</li> <li><strong>序列号:</strong> TCP 给发送的每一个包进行编号，接收方对数据包进行排序，把有序数据传送给应用层</li> <li><strong>确认应答：</strong> TCP传输的过程中，每次接收方收到数据后，都会对传输方进行确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号，告诉发送方，接收到了哪些数据，下一次的数据从哪里发</li> <li><strong>超时重传：</strong> 当 TCP 发出一个段后，它启动一个定时器，等待目的端确认收到这个报文段。如果不能及时收到一个确认，将重发这个报文段</li> <li><strong>连接管理：</strong> 连接管理就是三次握手与四次挥手的过程，在前面详细讲过这个过程，这里不再赘述。保证可靠的连接，是保证可靠性的前提</li> <li><strong>流量控制：</strong> TCP 连接的每一方都有固定大小的缓冲空间，TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据，能提示发送方降低发送的速率，防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 （TCP 利用滑动窗口实现流量控制）</li> <li><strong>拥塞控制：</strong> 当网络拥塞时，减少数据的发送</li> <li><strong>ARQ协议：</strong> 也是为了实现可靠传输的，它的基本原理就是每发完一个分组就停止发送，等待对方确认。在收到确认后再发下一个分组</li></ul> <h2 id="arq协议"><a href="#arq协议" class="header-anchor">#</a> ARQ协议</h2> <p><strong>自动重传请求</strong>（Automatic Repeat-reQuest，ARQ）是OSI模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制，在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认帧，它通常会重新发送。ARQ包括停止等待ARQ协议和连续ARQ协议。</p> <p><strong>停止等待ARQ协议</strong></p> <ul><li>停止等待协议是为了实现可靠传输的，它的基本原理就是每发完一个分组就停止发送，等待对方确认（回复ACK）。如果过了一段时间（超时时间后），还是没有收到 ACK 确认，说明没有发送成功，需要重新发送，直到收到确认后再发下一个分组</li> <li>在停止等待协议中，若接收方收到重复分组，就丢弃该分组，但同时还要发送确认
<strong>优点：</strong> 简单
<strong>缺点：</strong> 信道利用率低，等待时间长</li></ul> <p><strong>1. 无差错情况:</strong></p> <p>发送方发送分组,接收方在规定时间内收到,并且回复确认.发送方再次发送。</p> <p><strong>2. 出现差错情况（超时重传）：</strong></p> <p>停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认，就重传前面发送过的分组（认为刚才发送过的分组丢失了）。因此每发送完一个分组需要设置一个超时计时器，其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为<strong>自动重传请求ARQ</strong> 。另外在停止等待协议中若收到重复分组，就丢弃该分组，但同时还要发送确认。<strong>连续 ARQ 协议</strong>可提高信道利用率。发送维持一个发送窗口，凡位于发送窗口内的分组可连续发送出去，而不需要等待对方确认。接收方一般采用累积确认，对按序到达的最后一个分组发送确认，表明到这个分组位置的所有分组都已经正确收到了。</p> <p><strong>3. 确认丢失和确认迟到</strong></p> <ul><li><strong>确认丢失：</strong> 确认消息在传输过程丢失。当A发送M1消息，B收到后，B向A发送了一个M1确认消息，但却在传输过程中丢失。而A并不知道，在超时计时过后，A重传M1消息，B再次收到该消息后采取以下两点措施：1. 丢弃这个重复的M1消息，不向上层交付。 2. 向A发送确认消息。（不会认为已经发送过了，就不再发送。A能重传，就证明B的确认消息丢失）</li> <li><strong>确认迟到：</strong> 确认消息在传输过程中迟到。A发送M1消息，B收到并发送确认。在超时时间内没有收到确认消息，A重传M1消息，B仍然收到并继续发送确认消息（B收到了2份M1）。此时A收到了B第二次发送的确认消息。接着发送其他数据。过了一会，A收到了B第一次发送的对M1的确认消息（A也收到了2份确认消息）。处理如下：1. A收到重复的确认后，直接丢弃。2. B收到重复的M1后，也直接丢弃重复的M1</li></ul> <p><strong>连续ARQ协议</strong></p> <p>连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口，凡位于发送窗口内的分组可以连续发送出去，而不需要等待对方确认。接收方一般采用累计确认，对按序到达的最后一个分组发送确认，表明到这个分组为止的所有分组都已经正确收到了。</p> <p><strong>优点：</strong> 信道利用率高，容易实现，即使确认丢失，也不必重传</p> <p><strong>缺点：</strong> 不能向发送方反映出接收方已经正确收到的所有分组的信息。 比如：发送方发送了 5条 消息，中间第三条丢失（3号），这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落，而只好把后三个全部重传一次。这也叫 Go-Back-N（回退 N），表示需要退回来重传已经发送过的 N 个消息</p> <h2 id="流量控制"><a href="#流量控制" class="header-anchor">#</a> 流量控制</h2> <p><strong>TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率，保证接收方来得及接收。</strong> 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小，从而影响发送方的发送速率。将窗口字段设置为 0，则发送方不能发送数据。</p> <h2 id="拥塞控制"><a href="#拥塞控制" class="header-anchor">#</a> 拥塞控制</h2> <p>在某段时间，若对网络中某一资源的需求超过了该资源所能提供的可用部分，网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中，这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提，就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程，涉及到所有的主机，所有的路由器，以及与降低网络传输性能有关的所有因素。相反，流量控制往往是点对点通信量的控制，是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率，以便使接收端来得及接收。</p> <p>为了进行拥塞控制，TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度，并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。</p> <p>TCP的拥塞控制采用了四种算法，即<strong>慢开始、拥塞避免、快重传</strong>和<strong>快恢复</strong>。在网络层也可以使路由器采用适当的分组丢弃策略（如主动队列管理 AQM），以减少网络拥塞的发生。</p> <ul><li><strong>慢开始：</strong> 慢开始算法的思路是当主机开始发送数据时，如果立即把大量数据字节注入到网络，那么可能会引起网络阻塞，因为现在还不知道网络的符合情况。经验表明，较好的方法是先探测一下，即由小到大逐渐增大发送窗口，也就是由小到大逐渐增大拥塞窗口数值。cwnd初始值为1，每经过一个传播轮次，cwnd加倍</li> <li><strong>拥塞避免：</strong> 拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大，即每经过一个往返时间RTT就把发送放的cwnd加1</li> <li><strong>快重传与快恢复：</strong> 在 TCP/IP 中，快速重传和恢复（fast retransmit and recovery，FRR）是一种拥塞控制算法，它能快速恢复丢失的数据包。没有 FRR，如果数据包丢失了，TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内，没有新的或复制的数据包被发送。有了 FRR，如果接收机接收到一个不按顺序的数据段，它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认，它会假定确认件指出的数据段丢失了，并立即重传这些丢失的数据段。有了FRR，就不会因为重传时要求的暂停被耽误。当有单独的数据包丢失时，快速重传和恢复（FRR）能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时，它则不能很有效地工作</li></ul> <h2 id="服务端推送"><a href="#服务端推送" class="header-anchor">#</a> 服务端推送</h2> <p>HTTP是半双工协议，也就是说，在同一时刻数据只能单向流动，客户端向服务器发送请求(单向的)，然后服务器响应请求(单向的)，做不到服务器主动向客户端推送信息。可有些场景下，有的应用是供多个用户同时互动，服务端需要展示的数据经常变化，比如聊天室、滚动弹幕、比赛实况、协同编程、支付系统、多人游戏等等，这样的需求用HTTP来实现就相当地麻烦。在WebSocket协议之前，有三种实现双向通信的方式：</p> <ul><li><strong>轮询（polling）</strong>：轮询是客户端和服务器之间会一直进行连接，每隔一段时间就询问一次。其缺点也很明显：连接数会很多，一个接受，一个发送。轮询的间隔过长，会导致用户不能及时接收到更新的数据；轮询的间隔过短，会导致查询请求过多，增加服务器端的负担。</li> <li><strong>长轮询（long-polling）</strong>：长轮询是对轮询的改进版，客户端发送HTTP给服务器，服务器接到请求后Hold住连接，直到有消息才返回响应信息并关闭连接，客户端处理完响应信息后再向服务器发送新的请求。在无消息的情况下不会频繁的请求，耗资少，但是服务器Hold住连接会消耗资源。</li> <li><strong>iframe流（streaming）</strong>：iframe流方式是在页面中插入一个隐藏的iframe，利用其src属性在服务器和客户端之间创建一条长连接，服务器向iframe传输数据（通常是HTML，内有负责插入信息的javascript），在onload事件里，服务器timeout后再次重新加载iframe来实时更新页面，客户端只请求一次，然而服务端却是源源不断向客户端发送数据，维护这样的一个长连接会增加开销。</li></ul> <p><strong>WebSocket是基于TCP的一个持久化应用协议，支持双向通信</strong>，通过在请求头中增加Upgrade：websocket 及通信密钥Sec-WebSocket-Key，服务器返回状态码101 Switching Protocols响应客户端，使双方握手成功，建立全双工通信，不论服务器还是客户端，任意一方都可直接向对方发送报文。和HTTP相比，不但每次连接时的总开销减少，而且由于WebSocket的首部信息很小，通信量也相应减少。</p> <p><strong>Websocket是纯事件驱动的</strong>，一旦 WebSocket 连接建立后，只需要对WebSocket对象增加回调函数就可以监听事件处理到来的数据和改变的连接状态，节约带宽，节省服务器资源。在海量并发和客户端与服务器交互负载流量大的情况下，极大的节省了网络带宽资源的消耗，有明显的性能优势，且客户端发送和接受消息是在同一个持久连接上发起，实时性优势明显。</p></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">上次更新:</span> <span class="time">2/17/2021, 11:27:55 PM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/blog/dsm/architecture.html" class="prev">
        前端工程架构
      </a></span> <span class="next"><a href="/blog/dsm/node.html">
        服务端开发
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"><BackToTop></BackToTop><!----><Live2D></Live2D></div></div>
    <script src="/blog/assets/js/app.7fa07907.js" defer></script><script src="/blog/assets/js/2.b6c629b5.js" defer></script><script src="/blog/assets/js/20.6db77b80.js" defer></script><script src="/blog/assets/js/3.589a64d8.js" defer></script>
  </body>
</html>
